package cn.itcast.nems.journal.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Assert;
import cn.itcast.ic.common.exception.v2.BizExceptionProducer;
import cn.itcast.ic.msplatform.api.model.Dictionary;
import cn.itcast.nems.approval.bpm.eventstrategy.BpmEventStrategy;
import cn.itcast.nems.approval.dao.entity.ApprovalConfig;
import cn.itcast.nems.approval.dao.entity.ApprovalForm;
import cn.itcast.nems.approval.enumeration.ApprovalErrorBodyEnum;
import cn.itcast.nems.approval.enumeration.ApprovalFormStateEnum;
import cn.itcast.nems.approval.service.ApprovalAdditionalFormService;
import cn.itcast.nems.approval.service.ApprovalConfigService;
import cn.itcast.nems.approval.service.ApprovalFormFlowService;
import cn.itcast.nems.approval.service.ApprovalFormService;
import cn.itcast.nems.base.api.ClazzApi;
import cn.itcast.nems.base.api.SchoolApi;
import cn.itcast.nems.base.dao.entity.Paymentchannel;
import cn.itcast.nems.base.dto.ClazzDTO;
import cn.itcast.nems.base.dto.DictionaryDTO;
import cn.itcast.nems.base.dto.SchoolDTO;
import cn.itcast.nems.base.service.DictionaryService;
import cn.itcast.nems.common.PageQueryModel;
import cn.itcast.nems.common.account.AccountHolder;
import cn.itcast.nems.common.account.AccountInfo;
import cn.itcast.nems.common.enumeration.BizNumberTypeEnum;
import cn.itcast.nems.common.enumeration.NemsErrorBodyEnum;
import cn.itcast.nems.common.service.BizNumberService;
import cn.itcast.nems.common.util.NumberUtil;
import cn.itcast.nems.common.utils.WrapperUtil;
import cn.itcast.nems.employee.dao.entity.Employee;
import cn.itcast.nems.employee.service.EmployeeService;
import cn.itcast.nems.journal.dto.JournalRefundModel;
import cn.itcast.nems.journal.enumeration.JournalFinalErrorBodyEnum;
import cn.itcast.nems.journal.enumeration.JournalRefundErrorBodyEnum;
import cn.itcast.nems.journal.enumeration.JournalStateEnum;
import cn.itcast.nems.journal.service.JournalRefundService;
import cn.itcast.nems.journal.util.JournalRefundUtil;
import cn.itcast.nems.journal.util.JournalUtil;
import cn.itcast.nems.manager.activity.BpmWorkflowManager;
import cn.itcast.nems.manager.msas.MsasManager;
import cn.itcast.nems.manager.util.CustomBeanUtil;
import cn.itcast.nems.oldems.api.StudentApi;
import cn.itcast.nems.oldems.dto.Refund;
import cn.itcast.nems.oldems.dto.StudentDto;
import cn.itcast.nems.oldems.dto.oldapi.EmsApiResult;
import cn.itcast.nems.oldems.service.PaymentchannelService;
import cn.itcast.nems.oldems.service.sync.OldEmsSyncService;
import cn.itcast.nems.order.api.OrderApi;
import cn.itcast.nems.order.dao.entity.JournalRefund;
import cn.itcast.nems.order.dao.entity.JournalRefundFinance;
import cn.itcast.nems.order.dao.entity.JournalRefundRollback;
import cn.itcast.nems.order.dao.mapper.JournalRefundMapper;
import cn.itcast.nems.order.dto.OrderMode;
import cn.itcast.nems.order.service.JournalRefundFinanceService;
import cn.itcast.nems.order.service.JournalRefundRollbackService;
import cn.itcast.nems.order.service.OrderRefundService;
import cn.itcast.nems.studentrecord.constant.ReferenceTypeEnum;
import cn.itcast.nems.studentrecord.dao.condition.ApprovalType;
import cn.itcast.nems.studentrecord.dao.dataobject.JournalRefundDO;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecord;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecordFundFlow;
import cn.itcast.nems.studentrecord.dao.entity.StudentRecordStudentRelationship;
import cn.itcast.nems.studentrecord.dto.refund.JournalRefundDTO;
import cn.itcast.nems.studentrecord.dto.refund.JournalRefundRollbackDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordFundFlowDTO;
import cn.itcast.nems.studentrecord.dto.studentrecord.StudentRecordRefundDTO;
import cn.itcast.nems.studentrecord.enumeration.FundFlowPaymentTypeEnum;
import cn.itcast.nems.studentrecord.enumeration.StudentRecordErrorBodyEnum;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordFundFlowService;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordService;
import cn.itcast.nems.studentrecord.service.studentrecord.StudentRecordStudentRelationshipService;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.itcast.msas.sdk.acm.domain.AccountDto;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.time.LocalDateTime;
import java.util.*;

/**
 * <p>
 * 退费凭单-头 服务实现类
 * 注意点： 审批节点补充表单 ， 只有在审批完成之后才保存至  e2_journal_refund_finance ， 节点审批通过需要将表单信息保存至  e2_approval_additional_form
 * 审批日志 补充表单信息 通过 接口  "/b/bpm/approval/form/{form_id}/flow" 获取
 * </p>
 *
 * @author 刘曙
 * @since 2024-02-29
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class JournalRefundServiceImp extends ServiceImpl<JournalRefundMapper, JournalRefund>
        implements JournalRefundService, BpmEventStrategy {

    private final ApprovalFormFlowService approvalFormFlowService;
    private final ApprovalFormService approvalFormService;
    private final StudentRecordService studentRecordService;
    private final BizNumberService bizNumberService;
    private final ClazzApi clazzApi;
    private final StudentRecordFundFlowService studentRecordFundFlowService;
    private final SchoolApi schoolApi;
    private final PaymentchannelService paymentchannelService;
    private final ApprovalConfigService approvalConfigService;
    private final BpmWorkflowManager bpmWorkflowManager;
    private final StudentRecordStudentRelationshipService studentRecordStudentRelationshipService;
    private final OrderApi orderApi;
    private final OrderRefundService orderRefundService;
    private final DictionaryService dictionaryService;
    private final StudentApi studentApi;
    private final ApprovalAdditionalFormService approvalAdditionalFormService ;
    private final JournalRefundFinanceService journalRefundFinanceService ;
    private final JournalRefundRollbackService journalRefundRollbackService ;
    private final OldEmsSyncService oldEmsSyncService ;
    private final EmployeeService employeeService ;
    private final MsasManager msasManager ;

    private static final String DICT_REFUND_REASON = "NEMS_JOURNAL_REFUND_REASON";
    public final static String BUSINESS_LINE_NAME = "短训";
    public final static String BUSINESS_LINE_PARENT_CODE = "NEMS_YE_WU_XIAN";

    private  final  static  String REFUND_DROPOUT_NAME  =  "退费-学费" ;

    private Dictionary businessLine;


    @Value("${journal.check-form.refund}")
    private Boolean checkForm;

    @Override
    public String getStrategyName() {
        return ApprovalFormService.JournalType.REFUND.name();
    }

    @Override
    public void testFinish(String formId) {
        this.finish(formId);
    }

    private Dictionary getBusinessLine() {
        if (Objects.isNull(businessLine)) {
            DictionaryDTO dto = new DictionaryDTO();
            dto.setParentCode(BUSINESS_LINE_PARENT_CODE);
            dto.setName(BUSINESS_LINE_NAME);
            List<Dictionary> list = dictionaryService.list(dto);
            // List<Dictionary> list = dictionaryService.listByName(BUSINESS_LINE_PARENT_CODE, BUSINESS_LINE_NAME);
            Assert.notEmpty(list, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "短训业务线"));
            businessLine = list.get(0);
        }
        return businessLine;
    }

    @Override
    public IPage<JournalRefundDO> findPageList(PageQueryModel<JournalRefundModel> queryModel) {
        com.baomidou.mybatisplus.extension.plugins.pagination.Page<JournalRefundDO> page = new com.baomidou.mybatisplus.extension.plugins.pagination.Page<>();
        page.setCurrent(queryModel.getPageNumber());
        page.setSize(queryModel.getPageSize());
        JournalRefundModel refundModel = Objects.requireNonNullElse(queryModel.getCondition() ,  new JournalRefundModel()) ;
        JournalRefundDO refundDO = CustomBeanUtil.copyProperties(refundModel, new JournalRefundDO());
        refundDO.setMinAmount(Objects.nonNull(refundModel.getMinAmount())?NumberUtil.convertToCent(refundModel.getMinAmount()): null);
        refundDO.setMaxAmount(Objects.nonNull(refundModel.getMaxAmount())?NumberUtil.convertToCent(refundModel.getMaxAmount()):null);
        JournalUtil.invokeByApprovalType(this.getJournalIdFormIds(), refundDO, ApprovalFormService.JournalType.REFUND.name(), refundModel.getApprovalType());
        log.info("【退费凭单】分页查询条件：{}", JSON.toJSONString(refundDO));
        ApprovalType approvalType = refundDO.getApprovalType();
        if (approvalType == ApprovalType.ALL) {
            if (!AccountHolder.hasEmployeeGroups()) {
                return null;
            }
            refundDO.setEmployeeGroups(AccountHolder.getEmployeeGroups());
        }
        return getBaseMapper().findPageList(page, refundDO);
    }


    private List<String> getJournalIdFormIds() {
        ApprovalConfig config = approvalConfigService.getByJournalCode(ApprovalFormService.JournalType.REFUND.name());
        Assert.notNull(config, BizExceptionProducer.produce(ApprovalErrorBodyEnum.MUST_CONFIG_APPROVAL_CONFIG, ApprovalFormService.JournalType.REFUND.getCnName()));
        return bpmWorkflowManager.findWaitApproval(config.getBpmProcessKey(), AccountHolder.getRequiredAccount());
    }

    private void validateCreateOrUpdateParam(JournalRefundDTO refundDTO) {
        // 校验状态
        boolean stateFlag  = Objects.isNull(refundDTO.getState())
                || JournalStateEnum.DRAFT.name().equalsIgnoreCase(refundDTO.getState())
                || JournalStateEnum.PENDING.name().equalsIgnoreCase(refundDTO.getState()) ;
        Assert.isTrue(
                stateFlag,
                BizExceptionProducer.produce(JournalFinalErrorBodyEnum.JOURNAL_STATE_NOT_DRAFT)
        );
        refundDTO.setState(Objects.requireNonNullElse(refundDTO.getState(), JournalStateEnum.DRAFT.name()));
        // 学籍
        StudentRecord studentRecord = studentRecordService.getById(refundDTO.getStudentRecordId());
        Assert.notNull(studentRecord, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "学籍ID：" + refundDTO.getStudentRecordId()));
        // 退费类型
        String costTypeName = orderRefundService.findNameByCostTypeId(Integer.valueOf(refundDTO.getRefundTypeId()));
        Assert.notEmpty(costTypeName, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "退费类型ID:" + refundDTO.getRefundTypeId()));
        refundDTO.setRefundTypeName(costTypeName);
        // 验证退费金额
        int amount = NumberUtil.convertToCent(refundDTO.getAmount());
        studentRecordService.validateAmount(studentRecord  ,amount ,refundDTO.getState());
        // 学籍对应的班级信息
        ClazzDTO clazzDTO = clazzApi.queryByClassId(studentRecord.getClazzId());
        Assert.notNull(clazzDTO, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "班级ID：" + studentRecord.getClazzId()));
        refundDTO.setClazzId(clazzDTO.getClassId());
        refundDTO.setClazzName(clazzDTO.getClassName());
        // 默认 “短训” 业务线
        Dictionary dictionary = this.getBusinessLine();
        refundDTO.setBusinessLineId(dictionary.getId());
        refundDTO.setBusinessLineName(dictionary.getName());
        // 退学原因说明, 如果退费原因不为空 ， 需要校验 code
        if (StringUtils.hasText(refundDTO.getReasonCode())) {
            String reasonDescription = dictionaryService.getNameByParentCode(DICT_REFUND_REASON, refundDTO.getReasonCode());
            Assert.notNull(reasonDescription, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "退学原因CODE:" + refundDTO.getReasonCode()));
            refundDTO.setReasonDescription(reasonDescription);
        }
    }


    @Override
    public Boolean checkPaymentNoUnique(String paymentNo) {
        StudentRecordFundFlow studentRecordFundFlow = studentRecordFundFlowService.selectByStudentRecordIdPaymentNo(null ,paymentNo) ;
        log.info("【根据学籍ID和支付单号查询学籍流水】退费支付单号：{}，学籍流水查询结果：{}" , paymentNo , JSON.toJSONString(studentRecordFundFlow,SerializerFeature.DisableCircularReferenceDetect));
        return Objects.isNull(studentRecordFundFlow);
    }

    /**
     * <p>
     * 将【退费凭单】明细同步至老EMS
     * </p>
     *
     * @param refund 退费凭单头
     * @param journalRefundFinance 审批补充表单
     * @author zhouwei
     * @since 2024/4/2 7:42 PM
     **/
    public void syncToEMS(JournalRefund refund, JournalRefundFinance journalRefundFinance) {
        Paymentchannel paymentchannel = paymentchannelService.getById(journalRefundFinance.getPaymentAccountCode());
        Assert.notNull(paymentchannel ,  BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST ,"支付渠道ID：" + journalRefundFinance.getPaymentAccountCode() )) ;
        Refund requestParam = JournalRefundUtil.convertToRefund(refund, journalRefundFinance, paymentchannel.getSchoolId());
        StudentRecord studentRecord = studentRecordService.suspensionStudentRecord(refund.getStudentRecordId());
        requestParam.setJoinclassid(studentRecord.getId());
        List<StudentRecordStudentRelationship> relationships = studentRecordStudentRelationshipService.findByStudentRecordId(requestParam.getJoinclassid());
        Assert.notEmpty(relationships, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "学籍学生关关系-学籍ID：" + requestParam.getJoinclassid()));
        StudentDto studentDto = studentApi.findById(relationships.get(0).getStudentId());
        // 摘要
        requestParam.setName(StringUtils.hasText(refund.getReasonDescription()) ? refund.getReasonDescription() : ("退费" + "-" + studentDto.getName()));
        // 调用老EMS接口 同步退费单
        EmsApiResult result = oldEmsSyncService.refund(
                requestParam,
                AccountHolder.getRequiredAccountInfo().getEmail()
        );
        log.info("【退费凭单】同步至老EMS响应正文：{}", result);
    }
    /**
     * <p>
     * 将【退费退回】明细同步至老EMS
     * </p>
     *
     * @param refund 退费凭单头
     * @param journalRefundFinance 审批补充表单
     * @param refundRollback  退费退回
     * @author zhouwei
     * @since 2024/4/2 7:42 PM
     **/
    public void syncToEMS(JournalRefund refund, JournalRefundFinance journalRefundFinance,JournalRefundRollback refundRollback) {
        Paymentchannel paymentchannel = paymentchannelService.getById(refundRollback.getPaymentAccountCode());
        Assert.notNull(paymentchannel ,  BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST ,"支付渠道ID：" + refundRollback.getPaymentAccountCode() )) ;
        Refund requestParam = JournalRefundUtil.convertToRefund(refund, journalRefundFinance,refundRollback, paymentchannel.getSchoolId());
        StudentRecord studentRecord = studentRecordService.suspensionStudentRecord(refund.getStudentRecordId());
        requestParam.setJoinclassid(studentRecord.getId());
        List<StudentRecordStudentRelationship> relationships = studentRecordStudentRelationshipService.findByStudentRecordId(requestParam.getJoinclassid());
        Assert.notEmpty(relationships, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "学籍学生关关系-学籍ID：" + requestParam.getJoinclassid()));
        StudentDto studentDto = studentApi.findById(relationships.get(0).getStudentId());
        // 摘要
        requestParam.setName(StringUtils.hasText(refund.getReasonDescription()) ? refund.getReasonDescription() : ("退费退回" + "-" + studentDto.getName()));
        // 调用老EMS接口 同步退费单
        EmsApiResult result = oldEmsSyncService.refund(
                requestParam,
                AccountHolder.getRequiredAccountInfo().getEmail()
        );
        log.info("【退费退回】同步至老EMS响应正文：{}", result);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String create(JournalRefundDTO refundDTO) {
        // 校验参数
        this.validateCreateOrUpdateParam(refundDTO);
        // 保存草稿
        JournalRefund journal = CustomBeanUtil.copyProperties(refundDTO, new JournalRefund());
        journal.setBizDate(LocalDateTime.now());
        journal.setAmount(NumberUtil.convertToCent(refundDTO.getAmount()));
        journal.setId(bizNumberService.next(BizNumberTypeEnum.REFUND));
        journal.setState(JournalStateEnum.DRAFT.name());
        getBaseMapper().insert(journal);
        // 提交审批
        // 保存传DRAFT，提交传PENDING
        if (Objects.nonNull(refundDTO.getState()) && JournalStateEnum.PENDING.name().equalsIgnoreCase(refundDTO.getState())) {
            String result = this.startProcess(journal);
            log.info("【新建退费凭单】提交流程审批单结果：{}", result);
            lambdaUpdate().eq(JournalRefund::getId, journal.getId()).set(JournalRefund::getState, JournalStateEnum.PENDING.name()).update();
        }
        return journal.getId();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean addRollback(JournalRefundRollbackDTO refundRollbackDTO) {
        this.validateJournalRefundRollback(refundRollbackDTO);
        JournalRefundRollback refundRollback   =   CustomBeanUtil.copyProperties(refundRollbackDTO , new JournalRefundRollback()) ;
        refundRollback.setAmount(NumberUtil.convertToCent(refundRollbackDTO.getAmount())) ;
        JournalRefund journal  = getBaseMapper().selectById(refundRollbackDTO.getJournalRefundId()) ;
        Assert.notNull(journal , BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST , "退费凭单ID：" + refundRollbackDTO.getJournalRefundId())) ;
        JournalRefundFinance journalRefundFinance =   Objects.requireNonNullElse(journalRefundFinanceService.findByJournalRefundId(journal.getId()),new JournalRefundFinance()) ;
        journalRefundRollbackService.save(refundRollback) ;
        // 更新学籍相关流水明细
        this.refundStudentRecord(journal ,refundRollback) ;
        // 同步至老EMS
        this.syncToEMS(journal , journalRefundFinance,refundRollback);
        return Boolean.TRUE;
    }

    private void validateJournalRefundRollback(JournalRefundRollbackDTO refundRollbackDTO) {
        JournalRefund journalRefund = getBaseMapper().selectById(refundRollbackDTO.getJournalRefundId());
        Assert.notNull(journalRefund, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "凭单ID：" + refundRollbackDTO.getJournalRefundId()));
        Assert.isTrue(JournalStateEnum.FINISHED.name().equalsIgnoreCase(journalRefund.getState()), BizExceptionProducer.produce(JournalRefundErrorBodyEnum.JOURNAL_STATE_NOT_ROLLBACK));
        long count = journalRefundRollbackService.count(new LambdaQueryWrapper<JournalRefundRollback>().eq(JournalRefundRollback::getJournalRefundId, refundRollbackDTO.getJournalRefundId()).eq(JournalRefundRollback::getDeletedFlag, false));
        Assert.isFalse(count > 0, BizExceptionProducer.produce(JournalRefundErrorBodyEnum.JOURNAL_ROLLBACK_EXIST));
        StudentRecord studentRecord = studentRecordService.getById(journalRefund.getStudentRecordId());
        Assert.notNull(studentRecord, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "学籍ID：" + refundRollbackDTO.getStudentRecordId()));
        refundRollbackDTO.setStudentRecordId(journalRefund.getStudentRecordId());
        Integer amount =  NumberUtil.convertToCent(refundRollbackDTO.getAmount()) ;
        Assert.isTrue(journalRefund.getAmount().equals(amount) ,BizExceptionProducer.produce(JournalRefundErrorBodyEnum.JOURNAL_AMOUNT_DIFFERENT));
        // 渠道ID
        if (StringUtils.hasText(refundRollbackDTO.getPaymentAccountCode())) {
            Paymentchannel paymentchannel = paymentchannelService.getById(refundRollbackDTO.getPaymentAccountCode());
            Assert.notNull(paymentchannel, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "渠道ID:" + refundRollbackDTO.getPaymentAccountCode()));
            refundRollbackDTO.setPaymentAccountName(paymentchannel.getName());
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(@NonNull JournalRefundDTO refundDTO) {
        JournalRefund refund = getBaseMapper().selectOne(
                new LambdaQueryWrapper<JournalRefund>()
                        .eq(JournalRefund::getId,refundDTO.getId())
                        .eq(JournalRefund::getDeletedFlag ,false)
        ) ;
        Assert.notNull(refund , BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST ,"ID：" + refundDTO.getId())) ;
        // 检查是否存在重复 退费退回凭单
        if (Objects.nonNull(refundDTO.getJournalDropoutId())) {
            long count = getBaseMapper().selectCount(
                    new LambdaQueryWrapper<JournalRefund>()
                            .eq(JournalRefund::getJournalDropoutId, refundDTO.getJournalDropoutId())
                            .eq(JournalRefund::getDeletedFlag, false)
            );
            Assert.isFalse(count > 0, BizExceptionProducer.produce(NemsErrorBodyEnum.OPERATION_REPEAT, "退费退回"));
        }
        ;
        // 校验参数
        this.validateCreateOrUpdateParam(refundDTO);
        refund  =  CustomBeanUtil.copyProperties(refundDTO , new JournalRefund()) ;
        refund.setAmount(NumberUtil.convertToCent(refundDTO.getAmount())) ;
        getBaseMapper().updateById(refund) ;
        if (JournalStateEnum.PENDING.name().equalsIgnoreCase(refundDTO.getState())) {
            String result = this.startProcess(refund);
            log.info("【新建退费凭单】提交流程审批单结果：{}", result);
            lambdaUpdate().eq(JournalRefund::getId, refund.getId()).set(JournalRefund::getState, JournalStateEnum.PENDING.name()).update();
        }
        return Boolean.TRUE;
    }

    @Override
    public JournalRefundDTO findById(@NonNull String id) {
        JournalRefund refund = getBaseMapper().selectById(id);
        Assert.notNull(refund, "退费凭单ID：" + id);
        StudentRecord studentRecord = studentRecordService.getById(refund.getStudentRecordId());
        Assert.notNull(studentRecord, "学籍ID：" + refund.getStudentRecordId());
        // 根据学籍ID查询订单 ， 获取对应的付款方式
        OrderMode orderMode = orderApi.findByStudentRecordId(studentRecord.getId());
        List<StudentRecordStudentRelationship> relationships = studentRecordStudentRelationshipService.findByStudentRecordId(studentRecord.getId());
        Assert.notEmpty(relationships, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "学籍ID：[" + studentRecord.getId() + "]对应的学员关系记录"));
        var student = studentApi.findById(relationships.get(0).getStudentId());
        Assert.notNull(student, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "学员ID：" + relationships.get(0).getStudentId()));
        JournalRefundDTO refundDTO =  JournalRefundUtil.convert(refund, studentRecord, orderMode, relationships.get(0).getStudentId(), student);
        // 创建人
        Employee employee  =  employeeService.findOneByEmail(refund.getCreatedBy()) ;
        if(Objects.nonNull(employee)) {
            refundDTO.setCreatedBy(employee.getName()+ "（"+employee.getEmail()+"）");
        }
        // 退费退回记录
        JournalRefundRollback refundRollback   = journalRefundRollbackService.getOne(
                new WrapperUtil<JournalRefundRollback>().limitLaseOne("created_datetime")
                        .eq(JournalRefundRollback::getJournalRefundId, refund.getId())
                        .eq(JournalRefundRollback::getDeletedFlag ,false )
        ) ;
        if(Objects.nonNull(refundRollback)) {
            JournalRefundRollbackDTO refundRollbackDTO =  CustomBeanUtil.copyProperties(refundRollback, new JournalRefundRollbackDTO())  ;
            refundRollbackDTO.setAmount(NumberUtil.convertToYuan(refundRollback.getAmount()));
            // 退费退回创建人
            employee  =  employeeService.findOneByEmail(refund.getCreatedBy()) ;
            if(Objects.nonNull(employee)) {
                refundRollbackDTO.setCreatedBy(employee.getName()+ "（"+employee.getEmail()+"）");
            }
            // 校区
            if(Objects.nonNull(refundRollbackDTO.getPaymentAccountCode())) {
                Paymentchannel paymentchannel = paymentchannelService.getById(refundRollbackDTO.getPaymentAccountCode()) ;
                Assert.notNull(paymentchannel ,BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST ,"支付渠道ID："+ refundRollbackDTO.getPaymentAccountCode())) ;
                refundRollbackDTO.setCampusId(paymentchannel.getSchoolId());
                SchoolDTO schoolDTO = schoolApi.findById(paymentchannel.getSchoolId()) ;
                Assert.notNull(schoolDTO ,BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST ,"校区code："+ paymentchannel.getSchoolId())) ;
                refundRollbackDTO.setCampusName(schoolDTO.getName());
            }
            refundDTO.setRefundRollback(refundRollbackDTO);
        }
        // 审批单ID
        ApprovalForm approvalForm = approvalFormService.getOne(
                new WrapperUtil<ApprovalForm>().limitLaseOne("created_datetime").eq(ApprovalForm::getJournalId, id)
                        .eq(ApprovalForm::getDeletedFlag, false)
                        .notIn(ApprovalForm::getState, ApprovalFormStateEnum.CANCELED.name()));
        refundDTO.setFormId(Objects.nonNull(approvalForm) ? approvalForm.getId() : null);
        return  refundDTO ;
    }


    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(String id) {
        JournalRefund journal = baseMapper.selectById(id);
        Assert.notNull(journal, "数据不存在");
        Assert.isTrue("DRAFT".equals(journal.getState()), "只能删除草稿状态的数据");
        Assert.isTrue(journal.getCreatedBy().equals(AccountHolder.getRequiredAccount()), "只能删除自己创建的数据");
        return lambdaUpdate().eq(JournalRefund::getId, id)
                .set(JournalRefund::getDeletedBy, AccountHolder.getRequiredAccount())
                .set(JournalRefund::getDeletedDatetime, LocalDateTime.now())
                .set(JournalRefund::getDeletedFlag, Boolean.TRUE).update();
    }

    @Override
    public JournalRefundDO queryRefundJournalByStudentRecordId(String studentRecordId) {
        JournalRefund journalRefund = getBaseMapper().selectOne(
                new WrapperUtil<JournalRefund>()
                        .limitLaseOne("created_datetime")
                        .eq(JournalRefund::getStudentRecordId, studentRecordId)
                        .eq(JournalRefund::getDeletedFlag, false)
                        .eq(JournalRefund::getState, ApprovalFormStateEnum.FINISHED)
        );
        return Objects.isNull(journalRefund) ? null :
                CustomBeanUtil.copyProperties(journalRefund, new JournalRefundDO());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean flow(String id) {
        JournalRefund journal = getById(id);
        Assert.notNull(journal , BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST ,"退费凭单ID：" + id)) ;
        log.info("【提交】根据ID查询凭单头 ，ID：{}，查询结果：{}", id, JSON.toJSONString(journal));
        Assert.isTrue(JournalStateEnum.DRAFT.name().equalsIgnoreCase(journal.getState()),
                BizExceptionProducer.produce(StudentRecordErrorBodyEnum.REQUEST_JOURNAL_RENEWAL_STATE_ERROR));
        // 校验退费金额
        StudentRecord studentRecord   = studentRecordService.getById(journal.getStudentRecordId()) ;
        Assert.notNull(studentRecord ,BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST ,"学籍ID：" + journal.getStudentRecordId())) ;
        studentRecordService.validateAmount(studentRecord ,  journal.getAmount() , JournalStateEnum.PENDING.name());
        String result = this.startProcess(journal);
        log.info("【提交】流程审批单详情：{}", result);
        return lambdaUpdate()
                .eq(JournalRefund::getId, id)
                .set(JournalRefund::getState, JournalStateEnum.PENDING.name())
                .update();
    }

    /**
     * <p>
     * 启动流程
     * </p>
     *
     * @param params 退费凭单详情
     * @author zhouwei
     * @since 2024/3/28 5:05 PM
     **/
    private String startProcess(JournalRefund params) {
        // 锁定学籍
        studentRecordService.locks(Set.of(params.getStudentRecordId()), ReferenceTypeEnum.REFUND, params.getId());
        String id = params.getId();
        HashMap<String, Object> variables = new HashMap<>();
        // 2、是否特殊审批  special_approval （"true"， "false"）
        variables.put("special_approval", String.valueOf(params.getSpecialFlag()));
        // 经手部门
        variables.put("handling_department", "发起人部门");
        String start = approvalFormService.start(ApprovalFormService.JournalType.REFUND, id, variables, null);
        log.info("【提交】流程审批单详情：{}", start);
        return start;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void inform(String formID, String targetAssignee) {
        approvalFormFlowService.inform(formID, targetAssignee);
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Runnable finish(String formID) {
        ApprovalForm approvalForm = approvalFormService.getByBusinessKey(formID);
        if (approvalForm == null) {
            return null;
        }
        String journalId = approvalForm.getJournalId();
        log.info("【审批流程所有节点全部审批通过】查询审批单详情：{}", JSON.toJSONString(approvalForm));
        JournalRefund journal = getById(journalId);
        Assert.notNull(journal, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "退费凭单ID：" + journalId));
        // 处理历史凭单学籍流水、同步老EMS退费流水
        if (this.processHistoryStudentRecordFundFlow(journal)) {
            log.info("【审批流程所有节点全部审批通过】处理历史退费凭单ID：{}", journal.getId());
            return null;
        }
        if (JournalStateEnum.FINISHED.name().equalsIgnoreCase(journal.getState())) {
            log.info("【审批流程所有节点全部审批通过】已通过状态无需处理，ID：{}", journalId);
            return null;
        }
        log.info("【审批流程所有节点全部审批通过】查询凭单详情：{}", JSON.toJSONString(journal));
        // 处理补充表单
        JournalRefundFinance journalRefundFinance = this.wrapJournalRefundFinance(formID);
        log.info("【审批流程所有节点全部审批通过】checkForm：{}，journalRefundFinance：{}",
                checkForm,
                JSON.toJSONString(journalRefundFinance, SerializerFeature.DisableCircularReferenceDetect)
        );
        // 如果nacos配置文件中配置了必须校验附加表单，则将表单置为失败
        if (checkForm &&  !JournalRefundUtil.validateSuspensionForm(journalRefundFinance)) {
            doCheckFail(formID, journal.getId(), ApprovalErrorBodyEnum.MESSAGE_ADDITIONAL_FORM_FIELD_NOT_NULL);
            return null;
        }
        this.studentRecordService.unlocks(Set.of(journal.getStudentRecordId()), ReferenceTypeEnum.REFUND, journalId);
        lambdaUpdate().eq(JournalRefund::getId, journalId).set(JournalRefund::getState, JournalStateEnum.FINISHED.name()).update();
        log.info("【审批流程所有节点全部审批通过】修改状态 ID：{}， 修改状态：{}", journalId, JournalStateEnum.FINISHED.name());
        // 处理本地的审批流程单
        approvalFormService.finish(formID);
        if (Objects.nonNull(journalRefundFinance)) {
            // 手续费需要特殊处理
            journalRefundFinance.setCharge(NumberUtil.convertToCent(journalRefundFinance.getCharge())) ;
            AccountInfo accountInfo   = AccountHolder.getRequiredAccountInfo() ;
            try {
                AccountDto account = this.msasManager.getAccount(journalRefundFinance.getCreatedBy());
                AccountInfo financeAccount = new AccountInfo(journalRefundFinance.getCreatedBy(), account.getName(),  account.getEmployeeNumber(),"");
                AccountHolder.setAccountInfo(financeAccount);
                journalRefundFinance.setStudentRecordId(journal.getStudentRecordId()) ;
                journalRefundFinance.setJournalRefundId(journal.getId()) ;
                journalRefundFinanceService.save(journalRefundFinance) ;
            }finally {
                AccountHolder.setAccountInfo(accountInfo);
            }
            // 维护学籍流水、 同步老EMS退费流水
            Assert.notNull(journalRefundFinance, BizExceptionProducer.serverError("【审批流程所有节点全部审批通过】退费凭单ID：%s，补充表单为空", journal.getId()));
            this.refundStudentRecord(journal, journalRefundFinance);
            this.syncToEMS(journal, journalRefundFinance);
        }
        return null;
    }

    /**
     * <p>
     * 处理历史学籍流水
     * 1、增加学籍流水
     * 2、同步退费流水至老EMS
     * </p>
     *
     * @param journal 退费凭单头数据
     * @author zhouwei
     * @since 2024/4/16 4:54 PM
     **/
    private Boolean processHistoryStudentRecordFundFlow(JournalRefund journal) {
        JournalRefundFinance journalRefundFinance = journalRefundFinanceService.findByJournalRefundId(journal.getId());
        if (JournalStateEnum.FINISHED.name().equalsIgnoreCase(journal.getState()) && Objects.nonNull(journalRefundFinance)) {
            this.refundStudentRecord(journal, journalRefundFinance);
            this.syncToEMS(journal, journalRefundFinance);
            return true;
        }
        return false;
    }

    private JournalRefundFinance wrapJournalRefundFinance(String formId) {
        Map<String, Object> map = approvalAdditionalFormService.formAdditionalFroms(formId);
        log.info("【退费凭单】审批单ID：{} ，根据审批单ID查询补充表单信息：{}" , formId,JSON.toJSONString(map ,SerializerFeature.DisableCircularReferenceDetect));
        JournalRefundFinance journalRefundFinance;
        try {
            journalRefundFinance = JournalRefundUtil.OBJECT_MAPPER
                    .convertValue(map, JournalRefundFinance.class);

            return journalRefundFinance;
        } catch (Exception e) {
            log.error("【补充表单转退费凭单】附加表单转换失败 (final)", e);
            return null;
        }
    }


    /**
     * <p>
     * 【退费凭单】更新学籍流水相关逻辑 e2_student_record
     * </p>
     *
     * @author zhouwei
     * @since 2024/4/1 8:03 PM
     **/
    private void refundStudentRecord (JournalRefund refund ,@NonNull JournalRefundFinance journalRefundFinance) {
        StudentRecord studentRecord  =  studentRecordService.getById(refund.getStudentRecordId()) ;
        Assert.notNull(studentRecord  ,BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST,"学籍ID：" + refund.getStudentRecordId())) ;
        // 校验退费金额
        studentRecordService.validateAmount(studentRecord , refund.getAmount() ,refund.getState());
        ClazzDTO clazzDTO = clazzApi.queryByClassId(studentRecord.getClazzId()) ;
        Assert.notNull(clazzDTO  ,BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST,"班级ID：" + studentRecord.getClazzId())) ;
        //  检查退费凭单对应学籍流水是否重复
        List<StudentRecordFundFlow> recordFundFlowList = studentRecordFundFlowService.selectByStudentRecordIdAndReferenceIdAndReferenceType(refund.getStudentRecordId() , refund.getId(), ReferenceTypeEnum.REFUND.name()) ;
        Assert.isFalse(CollectionUtil.isNotEmpty(recordFundFlowList),BizExceptionProducer.produce(NemsErrorBodyEnum.OPERATION_REPEAT,String.format("退费凭单ID：%s学籍流水", refund.getId()))); ;
        // 更新学籍相关逻辑 e2_student_record
        StudentRecordRefundDTO refundDTO = new StudentRecordRefundDTO();
        refundDTO.setReferenceType(ReferenceTypeEnum.REFUND);
        refundDTO.setStudentRecordId(refund.getStudentRecordId());
        refundDTO.setReferenceId(refund.getId());
        refundDTO.setRefundAmount(refund.getAmount());
        // 对应审批补充表单 办理时间 ->  补充表单 【退费付款时间】
        refundDTO.setBizDate(journalRefundFinance.getPaymentDatetime());
        refundDTO.setUpdateReceivable(Boolean.TRUE);
        log.info("【退费凭单-修改学籍退费金额】参数：{}", JSON.toJSONString(refundDTO ,SerializerFeature.DisableCircularReferenceDetect));
        studentRecordService.refund(refundDTO ,true);
        // 生成学籍资金流转明细 “e2_student_record_fund_flow”
        StudentRecordFundFlowDTO fundFlow = new StudentRecordFundFlowDTO();
        fundFlow.setStudentRecordId(refund.getStudentRecordId());
        fundFlow.setClazzId(clazzDTO.getClassId());
        fundFlow.setClazzName(clazzDTO.getClassName());
        fundFlow.setReferenceType(ReferenceTypeEnum.REFUND);
        // 对应审批补充表单 办理时间 ->   补充表单 【退费付款时间】
        fundFlow.setBizDate(journalRefundFinance.getPaymentDatetime());
        // 对应审批补充表单办理时间
        fundFlow.setCreatedDatetime(journalRefundFinance.getBizDate());
        fundFlow.setReferenceId(refund.getId());
        fundFlow.setReferenceLineId(refund.getId() );
        fundFlow.setPaymentType(FundFlowPaymentTypeEnum.REFUND);
        fundFlow.setPaymentNo(StringUtils.hasText(journalRefundFinance.getPaymentNo())?journalRefundFinance.getPaymentNo():"");
        fundFlow.setAmount(-refund.getAmount());
        fundFlow.setCharge(journalRefundFinance.getCharge());
        fundFlow.setPaymentMethodCode("");
        fundFlow.setPaymentMethodName("");
        fundFlow.setPaymentAccountCode(journalRefundFinance.getPaymentAccountCode());
        fundFlow.setPaymentAccountName(journalRefundFinance.getPaymentAccountName());
        fundFlow.setExtend1(journalRefundFinance.getPosNo());
        // 学籍卡-支付信息 【摘要】 对应  审批补充表单 【摘要】
        fundFlow.setRemark(journalRefundFinance.getSummary());
        fundFlow.setUpdateStudentRecord(Boolean.FALSE);
        log.info("【退费凭单-增加学籍流水】参数：{}", JSON.toJSONString(fundFlow ,SerializerFeature.DisableCircularReferenceDetect));
        studentRecordFundFlowService.create(fundFlow);
    }
    /**
     * <p>
     * 【退费退回】更新学籍流水相关逻辑 e2_student_record
     * </p>
     *
     * @author zhouwei
     * @since 2024/4/1 8:03 PM
     **/
    private void refundStudentRecord (JournalRefund refund ,@NonNull JournalRefundRollback refundRollback) {
        StudentRecord studentRecord  =  studentRecordService.getById(refund.getStudentRecordId()) ;
        Assert.notNull(studentRecord  ,BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST,"学籍ID：" + refund.getStudentRecordId())) ;
        // 校验退费金额
        // studentRecordService.validateAmount(studentRecord , refund.getAmount() ,refund.getState());
        ClazzDTO clazzDTO = clazzApi.queryByClassId(studentRecord.getClazzId()) ;
        Assert.notNull(clazzDTO  ,BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST,"班级ID：" + studentRecord.getClazzId())) ;
        LocalDateTime currentDatetime = LocalDateTime.now() ;
        // 更新学籍相关逻辑 e2_student_record
        StudentRecordRefundDTO refundDTO = new StudentRecordRefundDTO();
        refundDTO.setReferenceType(ReferenceTypeEnum.REFUND);
        refundDTO.setStudentRecordId(refund.getStudentRecordId());
        refundDTO.setReferenceId(refund.getId());
        refundDTO.setRefundAmount(-refund.getAmount());
        // 办理时间， 取退费退回创建时间 ->  取退费退回退费付款时间
        // refundDTO.setBizDate(refundRollback.getRollbackDatetime());
        refundDTO.setBizDate(refundRollback.getRollbackDatetime());
        refundDTO.setUpdateReceivable(Boolean.TRUE);
        log.info("【退费退回-修改学籍退费金额】参数：{}", JSON.toJSONString(refundDTO ,SerializerFeature.DisableCircularReferenceDetect));
        studentRecordService.refund(refundDTO ,true);
        // 生成学籍资金流转明细 “e2_student_record_fund_flow”
        StudentRecordFundFlowDTO fundFlow = new StudentRecordFundFlowDTO();
        fundFlow.setStudentRecordId(refund.getStudentRecordId());
        fundFlow.setClazzId(clazzDTO.getClassId());
        fundFlow.setClazzName(clazzDTO.getClassName());
        fundFlow.setReferenceType(ReferenceTypeEnum.REFUND);
        // 办理时间， 取退费退回创建时间 ->  取退费退回退费付款时间
        // fundFlow.setBizDate(refundRollback.getCreatedDatetime());
        fundFlow.setBizDate(refundRollback.getRollbackDatetime());
        fundFlow.setReferenceId(refund.getId());
        fundFlow.setReferenceLineId(refund.getId() );
        fundFlow.setPaymentType(FundFlowPaymentTypeEnum.REFUND_ROLLBACK);
        // #TODO 临时处理一下
        fundFlow.setPaymentNo("");
        fundFlow.setAmount(refundRollback.getAmount());
        // fundFlow.setCharge(null);
        fundFlow.setPaymentMethodCode("");
        fundFlow.setPaymentMethodName("");
        fundFlow.setPaymentAccountCode(refundRollback.getPaymentAccountCode());
        fundFlow.setPaymentAccountName(refundRollback.getPaymentAccountName());
        fundFlow.setExtend1(refundRollback.getPosNo());
        //fundFlow.setRemark(refund.getRemark());
        fundFlow.setRemark(StringUtils.hasText(refundRollback.getBizNo()) ? "退费退回流水号：" +refundRollback.getBizNo(): null);
        fundFlow.setUpdateStudentRecord(Boolean.FALSE);
        log.info("【退费退回-增加学籍流水】参数：{}", JSON.toJSONString(fundFlow ,SerializerFeature.DisableCircularReferenceDetect));
        studentRecordFundFlowService.create(fundFlow);
    }

    /**
     * 表单校验不通过时将审批流程单设置为未通过，解锁学生
     *
     * @param formID    表单ID
     * @param journalId 凭单ID
     */
    private void doCheckFail(String formID, String journalId, ApprovalErrorBodyEnum errorBodyEnum) {
        JournalRefund journalRefund = getById(journalId);
        this.studentRecordService.unlocks(Set.of(journalRefund.getStudentRecordId()), ReferenceTypeEnum.REFUND, journalId);
        getBaseMapper().update(null,
                new LambdaUpdateWrapper<JournalRefund>().eq(JournalRefund::getId, journalId)
                        .set(JournalRefund::getState, JournalStateEnum.REJECTED.name()));
        approvalFormService.fail(formID, errorBodyEnum.getMessage());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void approve(String formID, ApprovalFormService.ApproveResult result, String comment,
                        final String form, Integer applyType) {
        ApprovalForm approvalForm = approvalFormService.getByBusinessKey(formID);
        if (approvalForm == null) {
            log.info("【审批完成】未匹配到数据：" + formID);
            return;
        }
        String journalId = approvalForm.getJournalId();
        JournalRefund journal = getById(journalId);
        log.info("【审批完成】查询审批单详情：{}", JSON.toJSONString(approvalForm));
        if (ApprovalFormService.ApproveResult.REJECT == result) {
            // 审批未通过
            lambdaUpdate().eq(JournalRefund::getId, journalId).set(JournalRefund::getState, JournalStateEnum.REJECTED.name()).update();
            studentRecordService.unlock(journal.getStudentRecordId());
        } else {
            JournalStateEnum journalStateEnum = JournalStateEnum.APPROVING;
            //  1：审批（默认））2：驳回到发起人）3：驳回上一级）
            if (applyType != null && applyType == 2) {
                journalStateEnum = JournalStateEnum.DRAFT;
                studentRecordService.unlock(journal.getStudentRecordId());
            } else if (applyType != null && applyType == 3) {
                journalStateEnum = JournalStateEnum.PENDING;
            }
            lambdaUpdate().eq(JournalRefund::getId, journalId).set(JournalRefund::getState, journalStateEnum.name()).update();
        }
        approvalFormService.approve(formID, result, comment, form, applyType);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void cancel(final String formID) {
        ApprovalForm approvalForm = approvalFormService.getByBusinessKey(formID);
        if (approvalForm == null) {
            log.info("【撤回】未匹配到审批单：" + formID);
            return;
        }
        String journalId = approvalForm.getJournalId();
        JournalRefund journal = getById(journalId);
        cn.hutool.core.lang.Assert.notNull(journal, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "ID：" + formID));
        // 校验：必须发起人才能撤回
        org.springframework.util.Assert.isTrue(journal.getCreatedBy().equals(AccountHolder.getRequiredAccount()), "必须发起人才能撤回");
        // 撤回-状态改为：草稿
        lambdaUpdate().eq(JournalRefund::getId, journalId).set(JournalRefund::getState, JournalStateEnum.DRAFT.name()).update();
        approvalFormService.cancel(formID);
        // 解锁学籍
        studentRecordService.unlock(journal.getStudentRecordId());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void rollbackRequester(String formID) {
        ApprovalForm approvalForm = approvalFormService.getByBusinessKey(formID);
        if (approvalForm == null) {
            log.info("【撤回】未匹配到审批单：" + formID);
            return;
        }
        String journalId = approvalForm.getJournalId();
        JournalRefund journal = getById(journalId);
        cn.hutool.core.lang.Assert.notNull(journal, BizExceptionProducer.produce(NemsErrorBodyEnum.DATA_EXIST, "ID：" + formID));
        if (journal.getState().equals(JournalStateEnum.DRAFT.name())) {
            return;
        }
        // 撤回-状态改为：草稿
        lambdaUpdate().eq(JournalRefund::getId, journalId).set(JournalRefund::getState, JournalStateEnum.DRAFT.name()).update();
//        approvalFormService.cancel(formID);
        // 解锁学籍
        studentRecordService.unlock(journal.getStudentRecordId());
    }

    @Override
    public void remind(String formID, String targetAssignee) {
        String result = approvalFormService.remind(formID, targetAssignee);
        log.info("【调用工作流催办接口】响应正文：" + result);
    }
}
